/*************************************************
* Title：TinyToolKit
* Author：Opportunity 
* Describe：实现对象池管理器的主要逻辑
* CreateTime：2020-04-05 21:33:30
* Version：1.0
* Modify Recorder：
*************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace TinyToolKit.ObjectPool
{
    public class TObjectPoolManager : ManagerBase, IObjectPoolManager
    {
        //存储所有的对象池，key是对象池的名称，方便索引
        private Dictionary<string, TObjectPool<GameObject>> prefabMap; 
        //存储场景中所有实例化的对象，无论显示还是隐藏，key是GUID,value是对象池名
        private Dictionary<int, string> instanceMap;

        public Dictionary<string, TObjectPool<GameObject>> PrefabMaps => prefabMap;

        /// <summary>
        /// 返回对象池管理中的对象池数量
        /// </summary>
        public int Count => prefabMap.Count;

        public override int Priority => 90;

        #region Manager Func
        public override void Init() 
        {
            prefabMap = new Dictionary<string, TObjectPool<GameObject>>();
            instanceMap = new Dictionary<int,string>();
        }

        public override void UpdateManager()
        {
            foreach (KeyValuePair<string, TObjectPool<GameObject>> item in prefabMap)
            {
                item.Value.Update(Time.deltaTime);
            }
        }

        public override void Shutdown()
        {
            foreach (KeyValuePair<string, TObjectPool<GameObject>> item in prefabMap)
            {
                item.Value.ShutDown();
            }

            prefabMap.Clear();
            instanceMap.Clear();
        }

        public override void OnSceneLoaded(Scene scene, LoadSceneMode mode)
        {
            base.OnSceneLoaded(scene, mode);

            Shutdown();
        }

        #endregion

        #region Public Func
        /// <summary>
        /// 判断是否已经存在poolName对应的对象池
        /// </summary>
        /// <param name="poolName">对象池名称</param>
        /// <returns>如果已经存在返回true，否则返回false</returns>
        public bool HasObjectPool(string poolName)
        {
            return prefabMap.ContainsKey(poolName);
        }

        /// <summary>
        /// 根据对象池名返回TObjectPool
        /// </summary>
        /// <param name="poolName">对象池名称</param>
        /// <returns>返回poolName对应的对象池类</returns>
        public TObjectPool<GameObject> GetObjectPool(string poolName)
        {
            TObjectPool<GameObject> pool;
            if (prefabMap.TryGetValue(poolName, out pool))
            {
                return pool;
            }
            TLogTool.Error("不存在{0}对应的对象池", poolName);
            return null;
        }

        /// <summary>
        /// 创建对象池
        /// </summary>
        /// <param name="poolName">对象池名</param>
        /// <param name="capacity">初始容量</param>
        /// <param name="spawnFunc">实例化对象的方法，传入的参数是当前实例化对象的顺序</param>
        /// <param name="destroyFunc">销毁对象执行的回调</param>
        /// <returns>返回对象池类</returns>
        public TObjectPool<GameObject> CreateObjectPool(string poolName, int capacity, Func<int, GameObject> spawnFunc, Action<GameObject> destroyFunc)
        {
            if (HasObjectPool(poolName))
            {
                TLogTool.Error("已经存在{0}对应的对象池，不应该重复创建", poolName);
                return GetObjectPool(poolName);
            }

            TObjectPool<GameObject> newPool = new TObjectPool<GameObject>(poolName, capacity, spawnFunc, destroyFunc);
            prefabMap.Add(poolName, newPool);
            
            return newPool;
        }

        /// <summary>
        /// 创建对象池
        /// </summary>
        /// <param name="poolName">对象池名</param>
        /// <param name="capacity">初始容量</param>
        /// <param name="prefab">实例化的对象</param>
        /// <param name="root">实例化对象的根结点</param>
        /// <returns>返回对象池类</returns>
        public TObjectPool<GameObject> CreateObjectPool(string poolName, int capacity, GameObject prefab, Transform root = null)
        {
            return CreateObjectPool(poolName,capacity, 
            index =>
            {
                GameObject newObj = GameObject.Instantiate(prefab);
                if (root)
                    root.SetStandardChild(newObj.transform);

                newObj.transform.Reset();
                newObj.SetActive(false);
                newObj.name = string.Format("{0}_{1}", prefab.name, index.ToString());
                return newObj;
            }, 
            obj =>
            {
                if (instanceMap.ContainsKey(obj.GetInstanceID()))
                {
                    instanceMap.Remove(obj.GetInstanceID());
                    GameObject.Destroy(obj);
                }
                else
                {
                    TLogTool.Error("{0}不是由{1}对象池生成的对象", obj.name, poolName);
                }  
            });
        }

        /// <summary>
        /// 销毁对象池
        /// </summary>
        /// <param name="poolName">对象池名称</param>
        /// <returns>销毁成功返回true，否则返回false</returns>
        public bool DestroyObjectPool(string poolName)
        {
            TObjectPool<GameObject> objectPool = GetObjectPool(poolName);
            if (objectPool != null)
            {
                objectPool.ShutDown();
                return prefabMap.Remove(poolName);
            }
            return false;
        }

        /// <summary>
        /// 实例化对象池中的对象
        /// </summary>
        /// <param name="poolName">对象池名</param>
        /// <returns>实例化的对象</returns>
        public GameObject SpawnObject(string poolName)
        {
            return SpawnObject(poolName, Vector3.zero, Quaternion.identity);
        }

        /// <summary>
        /// 实例化对象池中的对象
        /// </summary>
        /// <param name="poolName">对象池名</param>
        /// <param name="position">实例化对象的位置</param>
        /// <param name="rotation">实例化对象的四元数</param>
        /// <returns>实例化的对象</returns>
        public GameObject SpawnObject(string poolName, Vector3 position, Quaternion rotation)
        {
            TObjectPool<GameObject> objectPool = GetObjectPool(poolName);
            if (objectPool != null)
            {
                GameObject obj = objectPool.Spawn();
                obj.transform.localPosition = position;
                obj.transform.rotation = rotation;
                obj.SetActive(true);
                if (instanceMap.ContainsKey(obj.GetInstanceID()) == false)
                    instanceMap.Add(obj.GetInstanceID(), objectPool.Name);
                return obj;
            }
            return null;
        }

        /// <summary>
        /// 回收对象池中的对象
        /// </summary>
        /// <param name="obj">需要返还的对象</param>
        /// <returns>回收成功返回true，失败返回false</returns>
        public bool RecycleObject(GameObject obj)
        {
            if (instanceMap.TryGetValue(obj.GetInstanceID(),out var objectPoolName))
            {
                obj.SetActive(false);
                prefabMap[objectPoolName].Recycle(obj);
                return true;
            }

            TLogTool.Error($"{obj.name}不是从对象池生成的对象");
            return false;
        }
        #endregion
    }
}

